#ifndef __IM_MEMORY_H__
#define __IM_MEMORY_H__

#include <im_base_types.h>

/**
 * @defgroup IMMemory Memory handling
 * @ingroup PublicIME
 * @{
 */

IM_BEGIN_DECLS


/**
 * @brief Initialize the slice allocation system.
 *
 * It'll be called inside im_init (), so don't call
 * it directly.
 */
void        im_slice_init       ();

/**
 * @brief Allocate a memory slice with given size.
 */
IMPointer   im_slice_alloc      (IMSize         block_size);

/**
 * @brief Allocate a memory slice with given size,
 *        and fill it with zero.
 */
IMPointer   im_slice_alloc0     (IMSize         block_size);

/**
 * @brief Free a memory slice with specific size,
 *        which was allocated by im_slice_alloc() or
 *        im_slice_alloc0().
 */
void        im_slice_free       (IMSize         block_size,
                                 IMPointer      block);

/**
 * @brief Allocate a memory block with given size.
 *
 * It's optimized for allocating large memory block.
 *
 * Unlike im_slice_alloc(), the block size will be recorded,
 * so it's not necessary to specify the block size when freeing
 * it.
 *
 * Currently, it's just a wrapper of system malloc.
 *
 * The im_malloc() function shall allocate unused space for an object whose size
 * in bytes is specified by n_bytes and whose value is unspecified.
 *
 * The order and contiguity of storage allocated by successive calls 
 * to im_malloc() is unspecified. The pointer returned if the allocation 
 * succeeds shall be suitably aligned so that it may be assigned to a pointer 
 * to any type of object and then used to access such an object in the space allocated 
 * (until the space is explicitly freed or reallocated). 
 * Each such allocation shall yield a pointer to an object disjoint from any other object. 
 * The pointer returned points to the start (lowest byte address) of the allocated space. 
 * If the space cannot be allocated, a null pointer shall be returned. 
 * If the size of the space requested is 0, the behavior is implementation-defined: 
 * the value returned shall be either a null pointer or a unique pointer.
 *
 * @returns Upon successful completion with n_bytes not equal to 0, im_malloc() shall return a pointer to 
 * the allocated space. If n_bytes is 0, either a null pointer or a unique pointer that can be
 * successfully passed to im_free() shall be returned.
 * Otherwise, it shall return a null pointer.
 */
IMPointer   im_malloc           (IMSize         n_bytes);

/**
 * @brief Allocate a memory block with given size,
 *        and fill it with zero.
 *
 * The im_malloc0() function shall allocate unused space for an object whose size
 * in bytes is specified by n_bytes. The space shall be initialized to all bits to 0.
 *
 * The order and contiguity of storage allocated by successive calls to im_malloc0() is
 * unspecified. The pointer returned if the allocation succeeds shall be suitably aligned so that
 * it may be assigned to pointer to any type of object and then used to access such an object
 * in the space allocated (until the space is explicitly freed or reallocated).
 * Each such allocation shall yield a pointer to an object disjoint from any other object.
 * The pointer returned shall point to the start (lowest byte address) of the allocated space.
 * If the space cannot be allocated, a null pointer shall be returned.
 * If the size of the space requested is 0, the behavior is implementation-defined:
 * the value returned shall be either a null pointer or a unique pointer.
 *
 * @returns Upon successful completion with n_bytes not equal to 0, im_malloc0() shall return a pointer to
 * the allocated space. If n_bytes is 0, either a null pointer or a unique pointer that can be
 * successfully passed to im_free() shall be returned.
 * Otherwise, it shall return a null pointer.
 */
IMPointer   im_malloc0          (IMSize         n_bytes);

/**
 * @brief Resize a memory block which was allocated by
 *        im_malloc() or im_malloc0().
 *
 * The im_realloc() function shall change the size of the memory object pointed by mtm to the size
 * specified by n_bytes. The contents of the object shall remain unchanged up to the lesser of the new
 * and old sizes. If the new size of the memory object would require movement of the object,the space for 
 * the previous instantiation of the object is freed. If the new size is larger, the contents of the
 * newly allocated portion of the object are unspecified. If n_bytes is 0 and mem is not a null pointer,
 * the object pointed to is freed. If the space cannot be allocated, the object shall remain unchanged.
 *
 * If mem is a null pointer, im_realloc() shall be equivalent to im_malloc() for the specified size.
 * If mem does not match a pointer returned earlier by im_malloc0(), im_malloc() or im_realloc() or
 * if the space has previously been deallocated by a call to im_free() of im_realloc(),
 * the behavior is undefined.
 *
 * The order and contiguity of storage allocated by successive calls to im_realloc() ius unspecified.
 * The pointer returned if the allocation succeeds shall be suitably aligned so that it may be assigned to a poiter to any type of
 * object and then used to access such an object in the space allocated (until the space is explicitly
 * freed or reallocated). Each such allocation shall yield apointer to an object disjoint from any other object. 
 * The pointer returned shall point to the start (lowest byte address) of the allocated space. If the space cannot be allocated,
 * a null pointer shall be returned.
 *
 * @param mem Pointer to the memory block, if it's 0,
 *        a new memory block will be allocated.
 * @param n_bytes New size of the memory block.
 * @return im_realloc() funtion shall return a pointer to the (possibly moved) allocated space. If size is 0,
 * either a null pointer or a unique pointer that can be successfully passed to im_free() shall be returned. If there is not enough available memory, im_realloc() shall return a null pointer.
 */
IMPointer   im_realloc          (IMPointer      mem,
                                 IMSize         n_bytes);

/**
 * @brief Free a memory block which is allocated by
 *        im_malloc(), im_malloc0() or im_realloc().
 *
 * The im_free() function shall cause the space pointed to by mem to be deallocated;
 * that is, made available for further allocation.
 * if mem is a null pointer, no action shall occur. Otherwise, if tha argument does not
 * match a pointer earlier returned by the im_malloc(), im_malloc0(), im_realloc() function,
 * of if the space has been deallocated by a call to im_free() or im_realloc(), the behaivior is undefined.
 *
 * Any use of a pointer that refers to freed space results in undefined behavior.
 *
 * @return im_free() function shall not return a value.
 */
void        im_free             (IMPointer      mem);

#define im_new(struct_type,n_structs)           \
        ((struct_type *) im_malloc (((IMSize) sizeof (struct_type)) * ((IMSize) (n_structs))))
#define im_new0(struct_type,n_structs)          \
        ((struct_type *) im_malloc0 (((IMSize) sizeof (struct_type)) * ((IMSize) (n_structs))))
#define im_renew(struct_type,mem,n_structs)     \
        ((struct_type *) im_realloc ((mem), ((IMSize) sizeof (struct_type)) * ((IMSize) (n_structs))))

#define im_slice_new(struct_type)               \
        ((struct_type *) im_slice_alloc ((IMSize) sizeof (struct_type)))
#define im_slice_new0(struct_type)              \
        ((struct_type *) im_slice_alloc0 ((IMSize) sizeof (struct_type)))

/* we go through extra hoops to ensure type safety */
#define im_slice_delete(struct_type,mem)                do {        \
        if (1) im_slice_free((IMSize) sizeof (struct_type), mem);   \
        else (void)((struct_type*) 0 == (mem));                     \
        } while (0)


IM_END_DECLS

#endif
/** @} */
/*
vi:ts=4:nowrap:ai:expandtab
*/
